﻿/*
 * Copyright (c) 2012, University of Southampton All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of University of Southampton nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using LabTroveWebParts.LabTrovePosts;
using System.Collections.Generic;
using System.Net;
using System.IO;
using System.Xml;
using System.Globalization;
 
namespace LabTroveWebParts.LabTroveODataViewer
{
    public partial class LabTroveODataViewerUserControl : UserControl
    {
        public LabTroveODataViewer ParentWebPart { get; set; }

        /// <summary>
        /// Short name for the blog we are using
        /// </summary>
        public string blogShortName { get { return ParentWebPart.blogShortName; } }

        /// <summary>
        /// Master UID for the user
        /// </summary>
        public string blogUID { get { return ParentWebPart.blogUID; } }

        /// <summary>
        /// Base address of the blog site
        /// </summary>
        public string blogSite { get { return ParentWebPart.blogSite; } }

        /// <summary>
        /// True if the blog is public.
        /// </summary>
        public bool publicBlog { get { return ParentWebPart.publicBlog; } }

        /// <summary>
        /// Display "top" posts per page.
        /// </summary>
        public int top { get { return ParentWebPart.top; } }

        string uidUrlString;
        string topUrlString;
        string skipUrlString;

        protected int skip
        {
            get { return Convert.ToInt32(Request.QueryString["skip"]); }
        }

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            this.PreRender += new EventHandler(LabTroveODataViewerUserControl_PreRender);
        }

        protected void Page_Load(object sender, EventArgs e)
        {
        }


        private void LabTroveODataViewerUserControl_PreRender(object sender, EventArgs e)
        {
            string baseLabTroveUri = blogSite;

            if (baseLabTroveUri == null || blogUID == null || blogSite == null || blogShortName == null)
            {
                LiteralControl configureMeMsg = new LiteralControl();
                configureMeMsg.Text = "<h1>LabTrove e-Notebook Viewer</h1>";
                configureMeMsg.Text += "<p>Please use the Web Part Editor to configure your LabTrove server, user ID and blog name in order to use this web part.</p>";
                pnlPosts.Controls.Add(configureMeMsg);
            }
            else
            {

                if (baseLabTroveUri[baseLabTroveUri.Length - 1] != '/')
                    baseLabTroveUri += '/';
                // First - get OData feed.
                if (publicBlog)
                    uidUrlString = "";
                else
                    uidUrlString = "uid=" + blogUID;

                try
                {
                    DemoService labTroveService = new DemoService(new Uri(baseLabTroveUri + "odata/" + blogShortName));
                    labTroveService.SendingRequest += new EventHandler<System.Data.Services.Client.SendingRequestEventArgs>(labTroveService_SendingRequest);

                    // Then - work out URLs for the posts
                    List<Uri> postUris = new List<Uri>();
                    foreach (Post thisPost in labTroveService.Posts)
                    {
                        Uri uriThisPost = new Uri(baseLabTroveUri + blogShortName + "/" + thisPost.ID.ToString() + "/post.xml?" + uidUrlString);
                        postUris.Add(uriThisPost);
                    }

                    // If public box is checked but blog is not public, we get zero posts.  We may get zero some other way too.
                    if (postUris.Count == 0)
                    {
                        LiteralControl zeroMsg = new LiteralControl();
                        zeroMsg.Text = "<h1>No posts found</h1><p>No posts were found for blog &quot;" + blogShortName + "&quot; on site &quot;" + baseLabTroveUri + "&quot;.</p>";
                        if(publicBlog)
                            zeroMsg.Text += "<p>This blog was marked as public in the Edit Web Part dialog.  Incorrectly checking the Public box for a non-public blog may result in no posts being found.</p>";
                        pnlPosts.Controls.Add(zeroMsg);
                    }

                    // Header for paging.
                    // If skip==0, we can only go older (ie, $skip+=$top).  Else give back and forward links($skip+=$top, $skip-=$top).
                    LiteralControl litHdr = new LiteralControl();
                    litHdr.Text = "<hr />";
                    if (skip == 0)
                    {
                        litHdr.Text += string.Format("<a href={0}?skip={1}{2}>Older &gt;</a>", Page.Request.Url.AbsolutePath, (skip + top), 
                            (Page.Request.QueryString["postURI"]!=null) ? "&postUri=" + Page.Request.QueryString["postURI"] : "");
                    }
                    else
                    {
                        litHdr.Text += string.Format("<a href={0}?skip={1}{2}>&lt; Newer</a> &#124; ", 
                            Page.Request.Url.AbsolutePath, ((skip - top) > 0 ? (skip - top) : 0),
                            (Page.Request.QueryString["postURI"] != null) ? "&postUri=" + Page.Request.QueryString["postURI"] : "");
                        litHdr.Text += string.Format("<a href={0}?skip={1}{2}>Older &gt;</a>", Page.Request.Url.AbsolutePath, (skip + top),
                            (Page.Request.QueryString["postURI"] != null) ? "&postUri=" + Page.Request.QueryString["postURI"] : "");
                    }
                    litHdr.Text += "<hr />";
                    pnlPosts.Controls.Add(litHdr);

                    // Then - get the posts and display their HTML elements.
                    foreach (Uri postUri in postUris)
                    {
                        LiteralControl litPost = new LiteralControl();
                        litPost.Text = getLabTrovePostHTML(postUri);
                        pnlPosts.Controls.Add(litPost);
                    }

                    // Footer for paging.
                    LiteralControl litFtr = new LiteralControl();
                    litFtr.Text = litHdr.Text.Substring(6);
                    pnlPosts.Controls.Add(litFtr);
                }
                catch (Exception ex)
                {
                    LiteralControl errMsg = new LiteralControl();
                    if (ex.GetType() == typeof(System.Data.Services.Client.DataServiceQueryException))
                    {
                        errMsg.Text = "<h1>Data service error</h1>";
                        errMsg.Text += "<p>It was not possible to connect to the OData service at " + baseLabTroveUri + "odata/" + blogShortName + uidUrlString + "</p>";
                        errMsg.Text += "<p>Please check that you have correctly set the options in the Edit Web Part dialog, and verify that your LabTrove installation is functional</p>";
                    }
                    else
                    {
                        errMsg.Text = "<h1>Error</h1>";
                        errMsg.Text += "<p>An error of type " + ex.GetType() + " occured.  Please check that your LabTrove installation is functional";
                        errMsg.Text += "and that the settings in the Edit Web Part dialog are correct.  If the error persists, please report it as a bug.</p>";
                    }
                    pnlPosts.Controls.Add(errMsg);
                }
            }
        }

        private string newUrl;
        /// <summary>
        /// Replace the WebRequest to allow us to use the LabTrove UID get-query authentication mode.
        /// </summary>
        private void labTroveService_SendingRequest(object sender, System.Data.Services.Client.SendingRequestEventArgs e)
        {
            topUrlString = "$top=" + top.ToString();
            skipUrlString = "$skip=" + skip.ToString();

            if (uidUrlString != "")
            {
                newUrl = e.Request.RequestUri + "?" + uidUrlString + "&"  + topUrlString + "&" + skipUrlString;
            }
            else
            {
                newUrl = e.Request.RequestUri + "?" + topUrlString + "&" + skipUrlString;
            }
            WebRequest newHttpReq = WebRequest.Create(newUrl);

            e.Request = newHttpReq;
        }

        /// <summary>
        /// Returns a string containing the HTML for the post at the given Uri.
        /// </summary>
        private string getLabTrovePostHTML(Uri postUri)
        {
            WebRequest webReq = WebRequest.Create(postUri);
            webReq.Method = WebRequestMethods.Http.Post;
            WebResponse webResp = webReq.GetResponse();

            Stream receiveStream = webResp.GetResponseStream();
            XmlReaderSettings settings = new XmlReaderSettings();
            settings.ConformanceLevel = ConformanceLevel.Auto;
            XmlReader xr = XmlReader.Create(receiveStream, settings);
            xr.MoveToContent();
            
            String strHTML = null;
            String strTitle = null;
            String strTime = null;
            String strPost = null;
            String strID = null;
            String strSection = null;
            bool isPost = false;    // Comments and posts both have title, content, html, datestamp elements,
                                    // so set this only when we know we have a post rather than a comment

            while (xr.Read())
            {
                // Check depth to avoid taking "comment" entries as posts, posts have depth=2, comments have depth=4.
                if (xr.Name == "id" && xr.Depth == 2)
                    strID = xr.ReadElementContentAsString();
                if (xr.Name == "title" && xr.Depth==2)
                    strTitle = xr.ReadElementContentAsString();
                if (xr.Name == "section" && xr.Depth == 2)
                    strSection = xr.ReadElementContentAsString();
                if (xr.Name == "timestamp" && xr.Depth == 2)
                    strTime = xr.ReadElementContentAsString();
                if (xr.Name == "html" && xr.Depth == 2)
                {
                    isPost = true;
                    strHTML = xr.ReadInnerXml().Replace(@"<![CDATA[", "").Replace(@"]]>", "").Trim();
                }
            }
            
            if (isPost)
            {
                DateTime postTime = DateTime.Parse(strTime);
                CultureInfo ci = CultureInfo.CurrentCulture;

                strPost = "<h1 class=\"postTitle\">" + strTitle + "</h1>";
                strPost += "<div class=\"timestamp\">" + postTime.ToLocalTime().ToString("f",ci) + "</div><br />";
                strPost += "<div class=\"postTest\">" + strHTML + "</div>";
                if (Page.Request.QueryString["skip"] == null)
                {
                    strPost += string.Format("<div class=\"editBtn\"><a href=\"{0}?postURI={1}&postID={2}\">Edit post</a></div>",
                        Page.Request.Url.GetLeftPart(UriPartial.Path), postUri, strID);
                }
                else
                {
                    strPost += string.Format("<div class=\"editBtn\"><a href=\"{0}?postURI={1}&postID={2}&skip={3}\">Edit post</a></div>",
                        Page.Request.Url.GetLeftPart(UriPartial.Path), postUri, strID, Page.Request.QueryString["skip"]);
                }
                strPost += "<hr />";
                return strPost;
            }
            else
            {
                return "<p style=\"color:red\">Error: this does not look like a valid LabTrove post.</p>";
            }
        }

    }
}
